package org.xm.sk.service.data.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xm.sk.dao.ShopRepository;
import org.xm.sk.vo.ob.Local;
import org.xm.sk.vo.ob.PageConf;
import org.xm.sk.vo.tb.Shop;

@Service
@Transactional
public class ShopServiceImpl extends BaseServiceImpl<Shop> {

    private static final String CACHE_KEY = "'shop'";
    private static final String sk_CACHE_NAME = "shops";

    @Autowired
    private ShopRepository shopRepository;

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Shop findById(int id) {
        return this.shopRepository.getOne(id);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Set<Shop> findByName(String name) {
        return this.shopRepository.findByName(name);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_all'")
    public List<Shop> findAll() {
        return this.shopRepository.findAll();
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public List<Shop> findAll(Shop shop) {
        ExampleMatcher matcher = ExampleMatcher.matching();
        Example<Shop> ex = Example.of(shop, matcher);
        return this.shopRepository.findAll(ex);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0")
    public Object findAllByPage(PageConf pageConf) {
        List<Order> orders = new ArrayList<Order>();
        if (null != pageConf.getSortNames()) {
            for (int i = 0; i < pageConf.getSortNames().size(); i++) {
                orders.add(new Order(pageConf.getSortOrders().get(i),
                        pageConf.getSortNames().get(i)));
            }
        } else {
            orders.add(new Order(Direction.DESC, "id"));
        }
        Sort sort = Sort.by(orders);
        Pageable pageable = null;
        Specification<Shop> querySpecifi = new Specification<Shop>() {

            private static final long serialVersionUID = -6909126910425996764L;

            @Override
            public Predicate toPredicate(Root<Shop> root,
                    CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                Predicate predicate = cb.and(
                        cb.equal(root.get("datalevel").as(Integer.class), 0));
                List<Predicate> predicates = new ArrayList<>();
                if (null != pageConf.getSearch()
                        && 0 < pageConf.getSearch().trim().length()) {
                    String search = pageConf.getSearch().trim();
                    // 模糊查找
                    predicates.add(cb.like(root.get("name").as(String.class),
                            "%" + search + "%"));
                    predicates.add(
                            cb.like(root.get("modifydate").as(String.class),
                                    "%" + search + "%"));
                }
                if (predicates.isEmpty())
                    return predicate;
                // and到一起的话所有条件就是且关系，or就是或关系
                return cb.and(
                        cb.or(predicates
                                .toArray(new Predicate[predicates.size()])),
                        predicate);
            }
        };
        if (null != pageConf.getPage() && null != pageConf.getSize())
            pageable = PageRequest.of(pageConf.getPage(), pageConf.getSize(),
                    sort);
        if (null != pageable)
            return this.shopRepository.findAll(querySpecifi, pageable);
        else
            return this.shopRepository.findAll(querySpecifi, sort);
    }

    @Cacheable(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0+'_'+#p1")
    public Object findAllByPage(PageConf pageConf, Object object) {
        Pageable pageable = null;
        if (null != pageConf) {
            pageable = PageRequest.of(
                    -1 < pageConf.getPage() ? pageConf.getPage() : 0,
                    0 < pageConf.getSize() ? pageConf.getSize() : 100);
        }
        if (null != object && object instanceof Local) {
            Local local = (Local) object;
            if (null == local.getKeyStr()
                    || local.getKeyStr().trim().isEmpty()) {
                if (null == local.getRange() || 10.0 < local.getRange())
                    local.setRange(10.0);
                else if (0.5 > local.getRange())
                    local.setRange(0.5);
                return this.shopRepository.listByLocal(pageable,
                        local.getLongitude(), local.getLatitude(),
                        local.getRange());
            } else {
                return this.shopRepository.listByNameWithLocal(pageable,
                        local.getLongitude(), local.getLatitude(), local.getRange(),
                        "%" + local.getKeyStr() + "%");
            }
        } else if (null != object && object instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> map = (Map<String, Object>) object;
            if (null != map.get("userid")) {
                Integer userid = Integer.valueOf(map.get("userid").toString());
                return shopRepository.atList(pageable, userid);
            } else
                return null;
        } else {
            return findAllByPage(pageConf);
        }
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    // @CachePut(value = sk_CACHE_NAME, key = CACHE_KEY + "'_'+#p0.id")
    public Shop update(Shop shop) {
        shop.setEditer(user().getUserid());
        shop.setEditerName(user().getUsername());
        shop.setModifydate(new Date());
        return this.shopRepository.save(shop);
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(int id) {
        this.shopRepository.delete(id, user().getUserid(),
                user().getUsername());
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public void delete(Set<Integer> ids) {
        this.shopRepository.delete(ids, user().getUserid(),
                user().getUsername());
    }

    @CacheEvict(value = sk_CACHE_NAME, allEntries = true)
    public Shop add(Shop shop) {
        shop.setCreator(user().getUserid());
        shop.setEditer(user().getUserid());
        shop.setCreatorName(user().getUsername());
        shop.setEditerName(user().getUsername());
        return this.shopRepository.saveAndFlush(shop);
    }

}
